package com.kancy.swing.config;

import com.kancy.swing.lang.Asserts;
import com.kancy.swing.lang.StringUtils;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * ConfigProperties
 *
 * @author kancy
 * @date 2020/2/16 6:49
 */
public class ConfigProperties extends Properties {

    private static final Pattern PLACEHOLDER_REGEX = Pattern.compile("\\$\\{([^}]*)\\}");
    private static final String CLASS_PATH_RESOURCE_PREFIX = "classpath:";
    private String filePath;

    public ConfigProperties(String filePath) {
        this.filePath = filePath;
        loadProperties();
    }

    /**
     * 加载属性文件
     */
    void loadProperties() {
        loadProperties(this.filePath);
    }

    /**
     * 加载属性文件
     * @param filePath
     * @throws IOException
     */
    void loadProperties(String filePath) {
        Asserts.isTrue(StringUtils.isNotBlank(filePath), "properties file path is error.");

        InputStream resourceAsStream = null;
        try {
            if (filePath.startsWith(CLASS_PATH_RESOURCE_PREFIX)) {
                String resourceClassPath = filePath.replace(CLASS_PATH_RESOURCE_PREFIX, "").trim();
                resourceAsStream = ConfigProperties.class.getClassLoader().getResourceAsStream(resourceClassPath);
            } else {
                File file = new File(filePath);
                if (!file.exists() || file.isDirectory()) {
                    file.createNewFile();
                }
                resourceAsStream = new FileInputStream(file);
            }
            load(resourceAsStream);
        }catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (Objects.nonNull(resourceAsStream)){
                try {
                    resourceAsStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * 保存属性文件
     */
    void storeProperties() {
        if (!this.filePath.startsWith(CLASS_PATH_RESOURCE_PREFIX)){
            storeProperties(this.filePath);
        }
    }

    /**
     * 保存属性文件
     * @param filePath
     */
    void storeProperties(String filePath)  {
        Asserts.isTrue(StringUtils.isNotBlank(filePath), "properties file path is error.");

        BufferedWriter bufferedWriter = null;
        try {
            File file = new File(filePath);
            if (!file.exists() || file.isDirectory()){
                file.createNewFile();
            }

            bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));

            Properties tempProperties = new Properties();
            Iterator<Map.Entry<Object, Object>> iterator = this.entrySet().iterator();
            while (iterator.hasNext()){
                Map.Entry<Object, Object> next = iterator.next();
                if (next.getKey() instanceof String){
                    Object value = next.getValue();
                    tempProperties.setProperty(next.getKey().toString(), Objects.isNull(value) ? "" : String.valueOf(value));
                }
            }
            tempProperties.store(bufferedWriter, null);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (Objects.nonNull(bufferedWriter)){
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public String getStringProperty(String key){
        String property = getProperty(key,System.getProperty(key, System.getenv(key)));
        if (Objects.nonNull(property)){
            Matcher matcher = PLACEHOLDER_REGEX.matcher(property);
            while (matcher.find()){
                String subPropertyName = matcher.group(1).trim();
                String subProperty = getStringProperty(subPropertyName);
                if (Objects.isNull(subProperty)){
                    subProperty = System.getProperty(subPropertyName, System.getenv(subPropertyName));
                }
                property = property.replace(String.format("${%s}", matcher.group(1)), subProperty);
            }
        }
        return property;
    }

    public Boolean getBooleanProperty(String key){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            return Boolean.parseBoolean(property.trim());
        }
        return null;
    }
    public Boolean getBooleanProperty(String key, Boolean def){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            return Boolean.parseBoolean(property.trim());
        }
        return def;
    }

    public Integer getIntegerProperty(String key){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            return Integer.parseInt(property.trim());
        }
        return null;
    }
    public Integer getIntegerProperty(String key, Integer def){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            return Integer.parseInt(property.trim());
        }
        return def;
    }

    public Long getLongProperty(String key){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            return Long.parseLong(property.trim());
        }
        return null;
    }
    public Long getLongProperty(String key, Long def){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            return Long.parseLong(property.trim());
        }
        return def;
    }

    public Double getDoubleProperty(String key){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            return Double.parseDouble(property.trim());
        }
        return null;
    }
    public Double getDoubleProperty(String key, Double def){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            return Double.parseDouble(property.trim());
        }
        return def;
    }

    public List<String> getListProperty(String key){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            String[] arrays = property.split(",");
            final List<String> list = new ArrayList<>();
            for (String value : arrays) {
                if (StringUtils.isNotBlank(value)){
                    list.add(value);
                }
            }
            return list;
        }
        return Collections.emptyList();
    }

    public Set<String> getSetProperty(String key){
        String property = getStringProperty(key);
        if (Objects.nonNull(property)){
            String[] arrays = property.split(",");
            final Set<String> sets = new HashSet<>();
            for (String value : arrays) {
                if (StringUtils.isNotBlank(value)){
                    sets.add(value);
                }
            }
            return sets;
        }
        return Collections.emptySet();
    }

    public void setObjectProperty(String key, Object value){
        setProperty(key, Objects.isNull(value) ? null : String.valueOf(value));
    }

    public void setCollectionProperty(String key, Collection collection){
        if (Objects.nonNull(collection)){
            StringBuffer stringBuffer= new StringBuffer();
            collection.stream().forEach(item ->{
                if (Objects.nonNull(item)){
                    stringBuffer.append(",").append(item);
                }
            });
            if (stringBuffer.length() > 0){
                stringBuffer.deleteCharAt(0);
            }
            setProperty(key, stringBuffer.toString());
        }
    }

    public void setArrayProperty(String key, Object[] array){
        if (Objects.nonNull(array)){
            setCollectionProperty(key, Arrays.asList(array));
        }
    }
}
